/**************************************************************************************************
	$Header: /pub/cvsroot/mydns/src/lib/rr.c,v 1.43 2003/08/05 13:27:22 bboy Exp $

	Copyright (C) 2002-2003  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "mydns.h"

char mydns_rr_table_name[PATH_MAX] = MYDNS_RR_TABLE;
char *mydns_rr_where_clause = NULL;

/* Optional columns */
int mydns_rr_use_active = 0;
int mydns_rr_use_stamp = 0;


/**************************************************************************************************
	MYDNS_RR_COUNT
	Returns the number of records in the rr table.
**************************************************************************************************/
long
mydns_rr_count(sql)
	SQL *sql;
{
	return sql_count(sql, "SELECT COUNT(*) FROM %s", mydns_rr_table_name);
}
/*--- mydns_rr_count() --------------------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_SET_RR_TABLE_NAME
**************************************************************************************************/
void
mydns_set_rr_table_name(name)
	char *name;
{
	if (!name)
		strncpy(mydns_rr_table_name, MYDNS_RR_TABLE, sizeof(mydns_rr_table_name)-1);
	else
		strncpy(mydns_rr_table_name, name, sizeof(mydns_rr_table_name)-1);
}
/*--- mydns_set_rr_table_name() -----------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_SET_RR_WHERE_CLAUSE
**************************************************************************************************/
void
mydns_set_rr_where_clause(char *where)
{
	if (where && strlen(where))
	{
		if (!(mydns_rr_where_clause = strdup(where)))
			Errx("out of memory");
	}
}
/*--- mydns_set_rr_where_clause() ---------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_RR_GET_TYPE
**************************************************************************************************/
#if !PROFILING
inline
#endif
dns_qtype_t
mydns_rr_get_type(const char *type)
{
	switch (toupper(*type))
	{
		case 'A':
			if (type[1] == 'A')			/* `AAAA' */
				return DNS_QTYPE_AAAA;
#if ALIAS_ENABLED
			else if (type[1] == 'L')
				return DNS_QTYPE_ALIAS;
#endif
			else								/* `A' */
				return DNS_QTYPE_A;

		case 'C': return DNS_QTYPE_CNAME;
		case 'H': return DNS_QTYPE_HINFO;
		case 'M': return DNS_QTYPE_MX;
		case 'N': return DNS_QTYPE_NS;
		case 'T': return DNS_QTYPE_TXT;
		case 'P': return DNS_QTYPE_PTR;
		case 'S': return DNS_QTYPE_SRV;
	}
	return 0;
}
/*--- mydns_rr_get_type() -----------------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_PARSE_RR
**************************************************************************************************/
#if !PROFILING
inline
#endif
MYDNS_RR *
mydns_parse_rr(SQL_ROW row, const char *origin)
{
	MYDNS_RR *rv;

	if ((rv = (MYDNS_RR *)calloc(1, sizeof(MYDNS_RR))))
	{
		rv->next = NULL;

		rv->id = atou(row[0]);
		rv->zone = atou(row[1]);
		strncpy(rv->name, row[2], sizeof(rv->name)-1);
		strncpy(rv->data, row[3], sizeof(rv->data)-1);
		rv->aux = atou(row[4]);
		rv->ttl = atou(row[5]);
		rv->type = mydns_rr_get_type(row[6]);
#if ALIAS_ENABLED
		if (rv->type == DNS_QTYPE_ALIAS)
		{
			rv->type = DNS_QTYPE_A;
			rv->alias = 1;
		}
		else
			rv->alias = 0;
#endif

		/* Append origin to data if it's not there for these types: */
		if (origin)
			switch (rv->type)
			{
				case DNS_QTYPE_CNAME:
				case DNS_QTYPE_MX:
				case DNS_QTYPE_NS:
				case DNS_QTYPE_SRV:
					{
						int datalen = strlen(rv->data);
						if (datalen && rv->data[datalen-1] != '.')
						{
							strncat(rv->data, ".", sizeof(rv->data) - datalen - 1);
							strncat(rv->data, origin, sizeof(rv->data) - datalen - 2);
						}
					}
					break;
				default: break;
			}

		/* SRV records contain two unsigned 16-bit integers in the "data" field before
			the target, 'srv_weight' and 'srv_port' - parse them and make "data" contain
			only the target.  Also, make sure 'aux' fits into 16 bits,
			clamping values above 65535. */
		if (rv->type == DNS_QTYPE_SRV)
		{
			char *weight, *port, *target;

			/* Clamp 'aux' if necessary */
			if (rv->aux > 65535)
				rv->aux = 65535;

			/* Parse weight (into srv_weight), port (into srv_port), and target */
			target = rv->data;
			if ((weight = strsep(&target, " \t")))
			{
				rv->srv_weight = atoi(weight);
				if ((port = strsep(&target, " \t")))
					rv->srv_port = atoi(port);
				memmove(rv->data, target, strlen(target)+1);
			}
		}
	}
	return (rv);
}
/*-----------------------------------------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_RR_DUP
**************************************************************************************************/
MYDNS_RR *
mydns_rr_dup(start, recurse)
	MYDNS_RR *start;
	int recurse;
{
	register MYDNS_RR *first = NULL, *last = NULL, *rr, *s, *tmp;

	for (s = start; s; s = tmp)
	{
		tmp = s->next;

		if ((rr = (MYDNS_RR *)malloc(sizeof(MYDNS_RR))))
		{
			rr->id = s->id;
			rr->zone = s->zone;
			strncpy(rr->name, s->name, sizeof(rr->name)-1);
			rr->type = s->type;
			strncpy(rr->data, s->data, sizeof(rr->data)-1);
			rr->aux = s->aux;
			rr->ttl = s->ttl;
#if ALIAS_ENABLED
			rr->alias = s->alias;
#endif

			rr->srv_weight = s->srv_weight;
			rr->srv_port = s->srv_port;

			rr->next = NULL;
			if (recurse)
			{
				if (!first) first = rr;
				if (last) last->next = rr;
				last = rr;
			}
			else
				return (rr);
		}
	}
	return (first);
}
/*-----------------------------------------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_RR_SIZE
**************************************************************************************************/
#if !PROFILING
inline
#endif
size_t
mydns_rr_size(first)
	MYDNS_RR *first;
{
	register MYDNS_RR *p;
	register size_t size = 0;

	for (p = first; p; p = p->next)
		size += sizeof(MYDNS_RR);

	return (size);
}
/*-----------------------------------------------------------------------------------------------*/


/**************************************************************************************************
	_MYDNS_RR_FREE
	Frees the pointed-to structure.	Don't call this function directly, call the macro.
**************************************************************************************************/
#if !PROFILING
inline
#endif
void
_mydns_rr_free(MYDNS_RR *first)
{
	register MYDNS_RR *p, *tmp;

	for (p = first; p; p = tmp)
	{
		tmp = p->next;
		Free(p);
	}
}
/*-----------------------------------------------------------------------------------------------*/


/**************************************************************************************************
	MYDNS_RR_LOAD
	Returns 0 on success or nonzero if an error occurred.
**************************************************************************************************/
int
mydns_rr_load(SQL *sql, MYDNS_RR **rptr, uint32_t zone, dns_qtype_t type, char *name, char *origin)
{
	MYDNS_RR *first = NULL, *last = NULL;
	size_t	querylen;
	uchar		query[DNS_QUERYBUFSIZ],
				namequery[DNS_MAXNAMELEN + DNS_MAXNAMELEN + DNS_MAXNAMELEN + 25];
	uchar		*wheretype;
	register char *c, *cp;
	SQL_RES	*res;
	SQL_ROW	row;

	if (rptr) *rptr = NULL;

	/* Verify args */
	if (!sql || !rptr || !name)
	{
		errno = EINVAL;
		return (-1);
	}

	/* Get the type='XX' part of the WHERE clause */
	switch (type)
	{
#if ALIAS_ENABLED
		case DNS_QTYPE_A:			wheretype = " AND (type='A' OR type='ALIAS')"; break;
#else
		case DNS_QTYPE_A:			wheretype = " AND type='A'"; break;
#endif
		case DNS_QTYPE_AAAA:		wheretype = " AND type='AAAA'"; break;
		case DNS_QTYPE_CNAME:	wheretype = " AND type='CNAME'"; break;
		case DNS_QTYPE_HINFO:	wheretype = " AND type='HINFO'"; break;
		case DNS_QTYPE_MX:		wheretype = " AND type='MX'"; break;
		case DNS_QTYPE_NS:		wheretype = " AND type='NS'"; break;
		case DNS_QTYPE_PTR:		wheretype = " AND type='PTR'"; break;
		case DNS_QTYPE_SOA:		wheretype = " AND type='SOA'"; break;
		case DNS_QTYPE_SRV:		wheretype = " AND type='SRV'"; break;
		case DNS_QTYPE_TXT:		wheretype = " AND type='TXT'"; break;
		case DNS_QTYPE_ANY:		wheretype = ""; break;
		default:
			errno = EINVAL;
			return (-1);
	}

	/* Make sure 'name' and 'origin' (if present) are valid */
	for (c = name; *c; c++)
		if (SQL_BADCHAR(*c))
			return (0);
	for (c = origin; *c; c++)
		if (SQL_BADCHAR(*c))
			return (0);

	/* Construct query */
	if (origin)
	{
		if (!name[0])
			snprintf(namequery, sizeof(namequery), "(NAME='' OR name='%s')", origin);
		else
			snprintf(namequery, sizeof(namequery),
					   "(NAME='%s' OR name='%s.%s')", name, name, origin);
	}
	else
		snprintf(namequery, sizeof(namequery), "NAME='%s'", name);

	querylen = snprintf(query, sizeof(query),
		"SELECT "MYDNS_RR_FIELDS"%s FROM %s WHERE "
			"zone=%u%s AND %s%s%s",
			(mydns_rr_use_active ? ",active" : ""),
			mydns_rr_table_name,
			zone, wheretype, namequery,
			(mydns_rr_where_clause) ? " AND " : "",
			(mydns_rr_where_clause) ? mydns_rr_where_clause : "");

	/* Submit query */
	if (!(res = sql_query(sql, query, querylen)))
		return (-1);

	/* Add results to list */
	while ((row = sql_getrow(res)))
	{
		MYDNS_RR *new;

		/* Obey "active" column */
		if (mydns_rr_use_active && row[MYDNS_RR_NUMFIELDS] && !GETBOOL(row[MYDNS_RR_NUMFIELDS]))
			continue;

		if (!(new = mydns_parse_rr(row, origin)))
		{
			sql_free(res);
			return (-1);
		}

		/* Always trim origin from name (XXX: Why? When did I add this?) */
		/* Apparently removing this code breaks RRs where the name IS the origin */
		if (origin && (cp = strstr(new->name, origin)))
			*cp = '\0';

		if (!first) first = new;
		if (last) last->next = new;
		last = new;
	}

	*rptr = first;
	sql_free(res);
	return (0);
}
/*-----------------------------------------------------------------------------------------------*/

/* vi:set ts=3: */
